import java.util.Arrays;
import java.util.Random;
/**
 * the class calculate the fitness of the networksS 
 */
public class FitnessCalculator {

	private int [] [] inputExamples;
	private int [] [] sortedInputExamples;
	private Random rand = new Random();
	private FitnessCalculatorPopulationBuilder builder=null;
	
	/**
	 * C'tor
	 */
	public FitnessCalculator() {
		inputExamples = new int [Constants.EXAMPLES_NUM][Constants.NETWORK_SIZE_INPUT];
		sortedInputExamples = new int [Constants.EXAMPLES_NUM][Constants.NETWORK_SIZE_INPUT];
		builder=new FitnessCalculatorPopulationBuilder();
		createRandomInputExamples();

	}
	
	/***
	 * make random examples to determine by them how good the network
	 */
	private void createRandomInputExamples() {
		
		for (int i = 0; i < Constants.EXAMPLES_NUM; i++) {
			for (int j = 0; j < Constants.NETWORK_SIZE_INPUT; j++) {
				inputExamples[i][j] = rand.nextInt(Constants.INPUT_NUMBERS_BOUND);
			}
			
		}
		createSortedInputExamples();

		
	}
	/**
	 *make all the sorted input examples praller the examples 
	 */
	private void createSortedInputExamples(){
		for (int i = 0; i < Constants.EXAMPLES_NUM; i++) {
			sortedInputExamples[i]=Arrays.copyOf(inputExamples[i], sortedInputExamples[i].length);
			Arrays.sort(sortedInputExamples[i]);
		}
	}
	
	/**
	 * 
	 * @return the input examples
	 */
	public int[][] getInputExamples() {
		return inputExamples;
	}

	/**
	 * 
	 * @return the sorted input examples
	 */
	public int[][] getSortedInputExamples() {
		return sortedInputExamples;
	}
	
	/**
	 * 
	 * @param sortingNetwork - a network from the current generation
	 * @return the avg of the numbers that after running the example on the network placed correct
	 * according to the sorting inputs
	 */
	public double placedCorrect(SortingNetwork sortingNetwork )
	{
		int counter=0;
		for (int i=0 ; i<Constants.EXAMPLES_NUM; i++)
		{
			int[] output=sortingNetwork.sortInput(inputExamples[i]);
			for (int j=0 ; j<Constants.NETWORK_SIZE_INPUT;  j++)
			{
				if(output[j]==sortedInputExamples[i][j])
					counter++;
			}
		}
		return 1.0*counter/ Constants.EXAMPLES_NUM;
	}
	

	/**
	 * the function calculate the fitness of the network- by the ratio how much placed correct 
	 * and how much components it used
	 * @param sortingNetwork - a network 
	 * @return the fitness of the current network
	 */
	public int calculateFitness(SortingNetwork sortingNetwork) {
		
		double fitness=placedCorrect(sortingNetwork);
		double size =(double)sortingNetwork.getNetwork().size(); 
		
		double fi = Math.pow(fitness, Constants.AVG_SORTING_VALUE_POWER);
		double si = Math.pow(size, Constants.SIZE_POWER);
		
		fitness=1000*fi/si;
		return (int)fitness;
		
	}

	/**
	 * build new population while activating all the genetic operations 
	 * @param iteration
	 * @param population
	 * @param fitness
	 */
	public void evolveInputsToNextGenration(int iteration,
			SortingNetwork[] population,int[] fitness) {
		//calculate fitness
		int inputsFitness[] = new int [Constants.EXAMPLES_NUM];
		SortingNetwork[] kBest= builder.extractBestKNetworks(population,fitness);
		for (int i = 0; i < Constants.EXAMPLES_NUM; i++) {
			inputsFitness[i]= builder.calcuateFitenss(kBest,i,inputExamples,sortedInputExamples);
		}
		int[][] newInputPopulation=new int[Constants.EXAMPLES_NUM][Constants.NETWORK_SIZE_INPUT];
		builder.selectElitism(inputExamples,newInputPopulation,inputsFitness,Constants.ELITISM_COUNT);
		builder.doCrossovers(inputExamples,newInputPopulation,inputsFitness,Constants.EXAMPLES_NUM-Constants.ELITISM_COUNT);
		
		inputExamples=newInputPopulation;
		createSortedInputExamples();
		
	}
	
}


